package com.javenwong.android.engine.biology;

import java.io.DataInputStream;
import java.io.IOException;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;

import com.javenwong.android.engine.GameActivity;
import com.javenwong.android.engine.Graphics;
import com.javenwong.android.engine.util.MyHashMap;

/**
 * 锟斤拷锟斤拷
 */
public final class Animation {

	private static MyHashMap keys = new MyHashMap();

	/** 锟斤拷锟斤拷锟斤拷锟斤拷 */
	private static MyHashMap animations = new MyHashMap();

	/** 图片锟斤拷源,锟侥硷拷锟斤拷为 key */
	private static MyHashMap imagesSet = new MyHashMap();

	/** 图片锟斤拷锟斤拷片锟斤拷息,锟侥硷拷锟斤拷为 key */
	private static MyHashMap sliceSet = new MyHashMap();

	/** 图片 */
	private Bitmap[] slicesImage;

	/** 锟斤拷片锟斤拷辍拷呖锟? */
	private byte[][] slices;

	/** 锟斤拷锟斤拷帧 */
	private byte[][] frames;

	/** 锟斤拷撞 */
	private byte[][] collision;
	/** 帧宽、高 */
	private int[][] whs;
	/** 锟斤拷转 */
	private int transform;
	/** 锟斤拷前帧 */
	private byte frameIndex;
	/** 帧锟斤拷 */
	private byte frameNum;
	/** 帧锟斤拷时 */
	public int[] delays;
	private long timer;
	public int anix, aniy;
	private boolean end;
	private boolean visible = true;
	private int frameWidth, frameHeight;
	private int width, height;

	private Animation() {
	}

	private Animation(byte num) {
		frameNum = num;
		frames = new byte[num][];
		collision = new byte[num][];
		whs = new int[num][];
		delays = new int[num];
	}

	public byte getFrameIndex() {
		return frameIndex;
	}

	public byte getFrameCount() {
		return frameNum;
	}

	/** 锟斤拷锟斤拷 */
	private static Animation copy(Animation ani) {
		if (ani == null) {
			return null;
		}
		Animation copy = new Animation();
		copy.slicesImage = ani.slicesImage;
		copy.slices = ani.slices;
		copy.frames = ani.frames;
		copy.collision = new byte[ani.collision.length][];
		for (int i = 0; i < copy.collision.length; i++) {
			copy.collision[i] = new byte[ani.collision[i].length];
			System.arraycopy(ani.collision[i], 0, copy.collision[i], 0, ani.collision[i].length);
		}
		copy.delays = ani.delays;
		copy.whs = ani.whs;
		copy.frameNum = ani.frameNum;
		copy.transform = ani.transform;
		copy.width = ani.width;
		copy.height = ani.height;
		return copy;
	}

	public static Animation getAnimation(String id) {

		Animation ani = (Animation) animations.get(id);
		if (ani != null) {
			return copy(ani);
		} else {
			int n = id.lastIndexOf('_');
			if (n > 0) {
				String key = id.substring(0, n);
				if (!keys.containsKey(key)) {

					loadAnimations(key);
					return (Animation) animations.get(id);
				}
			}
		}
		return null;
	}

	public static Animation getAnimation(String id, Animation mirror) {
		Animation ani = getAnimation(id);
		if (ani == null && mirror != null) {
			ani = copy(mirror).mirror();
			animations.put(id, ani);
			return ani;
		}
		return ani;
	}

	public static void clear() {
		keys.clear();
		imagesSet.clear();
		sliceSet.clear();
		animations.clear();
		System.gc();
	}

	public void destroyBitmap() {
		for (Bitmap b : slicesImage) {
			b.recycle();
		}
	}

	// 已废弃
	// public final boolean loopFrame() {
	// if (++timer > delay) {
	// timer = 0;
	// frameIndex++;
	// if (frameIndex == frameNum) {
	// end = true;
	// frameIndex = 0;
	// return true;
	// }
	// }
	// return false;
	// }
	//
	// public final boolean nextFrame() {
	// if (++timer > delay) {
	// timer = 0;
	// frameIndex++;
	// if (frameIndex == frameNum) {
	// end = true;
	// frameIndex--;
	// return true;
	// }
	// }
	// return false;
	// }

	public final boolean loopFrame(long timeCha) {
		timer += timeCha;

		if (timer >= delays[frameIndex]) {
			// timer = timer-delay;
			timer = 0;

			frameIndex++;
			if (frameIndex == frameNum) {
				end = true;
				frameIndex = 0;
				return true;
			}
		}

		return false;
	}

	public final boolean nextFrame(long timeCha) {
		timer += timeCha;
		if (timer >= delays[frameIndex]) {
			// timer = timer-delay;
			timer = 0;

			frameIndex++;
			if (frameIndex == frameNum) {
				end = true;
				frameIndex--;
				return true;
			}
		}

		return false;
	}

	public boolean isEnd() {
		return end;
	}

	public boolean isLastFrame() {
		return frameIndex == frameNum - 1;
	}

	public Animation mirror() {
		for (int i = 0; i < collision.length; i++) {
			int x1 = collision[i][0];
			int x2 = collision[i][1];
			collision[i][0] = (byte) -x2;
			collision[i][1] = (byte) -x1;
		}
		transform = transform == 0 ? 2 : 0;
		return this;
	}

	public static Animation getMirror(Animation ani) {
		return copy(ani).mirror();
	}

	public boolean collided(int x, int y, int width, int height) {
		if (timer > 0) {
			return false;
		}
		int x1 = collision[frameIndex][0];
		int x2 = collision[frameIndex][1];

		if (x1 == x2) {
			return false;
		}

		int y1 = collision[frameIndex][2];
		int y2 = collision[frameIndex][3];

		int tx2 = x + width, ty2 = y + height;

		if (x > x2 || tx2 < x1 || y > y2 || ty2 < y1) {
			return false;
		}
		return true;
	}

	public boolean collided(int index, int x, int y, int width, int height) {
		int x1 = collision[index][0];
		int x2 = collision[index][1];

		if (x1 == x2) {
			return false;
		}

		int y1 = collision[index][2];
		int y2 = collision[index][3];

		int tx2 = x + width, ty2 = y + height;

		if (x > x2 || tx2 < x1 || y > y2 || ty2 < y1) {
			return false;
		}
		return true;
	}

	public final void drawFrame(Canvas g) {
		drawFrame(frameIndex, anix, aniy, g);
	}

	public final void drawFrame(int x, int y, Canvas g) {
		drawFrame(frameIndex, x, y, g);
	}

	public final void drawFrame(int frameIndex, int targetx, int targety, Canvas g) {

		if (!visible)
			return;

		frameWidth = whs[frameIndex][0];
		frameHeight = whs[frameIndex][1];

		byte[] frm = frames[frameIndex];
		int len = frm.length / 7; // double
		int index = 0;
		for (int i = 0; i < len; i++) {
			int img = frm[index++] & 0xff;
			int n = frm[index++] & 0xff;
			// int scIndex = (n << 2) + (n << 1); // double
			int scIndex = n * 8;
			int tx = (short) (((frm[index++] & 0xff) << 8) + (frm[index++] & 0xff)); // x
			int ty = (short) (((frm[index++] & 0xff) << 8) + (frm[index++] & 0xff)); // y
			int trans = frm[index++] & 0xff; // transform
			byte[] sc = slices[img];
			int x = ((sc[scIndex++] & 0xff) << 8) + (sc[scIndex++] & 0xff); // double
			int y = ((sc[scIndex++] & 0xff) << 8) + (sc[scIndex++] & 0xff); // double
			// int w = sc[scIndex++] & 0xff, h = sc[scIndex] & 0xff;
			int w = ((sc[scIndex++] & 0xff) << 8) + (sc[scIndex++] & 0xff); // double
			int h = ((sc[scIndex++] & 0xff) << 8) + (sc[scIndex++] & 0xff); // double
			if (transform == 2) {
				ty += targety;
				if (trans == 1) { // TRANS_MIRROR_ROT180
					trans = 3; // TRANS_ROT180
					tx = -(tx + w) + targetx;
				} else if (trans == 2) { // TRANS_MIRROR
					trans = 0; // TRANS_NONE
					tx = -(tx + w) + targetx;
				} else if (trans == 3) {
					trans = 1; // RANS_MIRROR_ROT180
					tx = -(tx + w) + targetx;
				} else if (trans == 4) { // TRANS_MIRROR_ROT270
					trans = 5; // TRANS_ROT90 ***********
					tx = -(tx + h) + targetx;

				} else if (trans == 5) { // TRANS_ROT90
					trans = 4; // TRANS_MIRROR_ROT270
					tx = -(tx + h) + targetx;
				} else if (trans == 6) { // TRANS_ROT270
					trans = 7; // TRANS_MIRROR_ROT90 ***********
					tx = -(tx + h) + targetx;

				} else if (trans == 7) { // TRANS_MIRROR_ROT90
					trans = 6; // TRANS_ROT270 ***********
					tx = -(tx + h) + targetx;

				} else {
					trans = transform;
					tx = -(tx + w) + targetx;
				}
				// g.drawRegion(slicesImage[img], x, y, w, h, trans, tx, ty,
				// 20);
				Graphics.drawRegion(g, slicesImage[img], x, y, w, h, trans, tx, ty, 20);
			} else {
				tx += targetx;
				ty += targety;
				// g.drawRegion(slicesImage[img], x, y, w, h, trans, tx, ty,
				// 20);
				Graphics.drawRegion(g, slicesImage[img], x, y, w, h, trans, tx, ty, 20);
			}

		}

		// ********** 画 坐标 + **********

		// g.setColor(0xff0000);
		// g.drawRect(collision[frameIndex][0] + targetx,
		// collision[frameIndex][2] + targety, collision[frameIndex][1]
		// - collision[frameIndex][0], collision[frameIndex][3] -
		// collision[frameIndex][2]);
		//
		// g.setColor(-1);
		// g.drawLine(targetx - 4, targety, targetx + 4, targety);
		// g.drawLine(targetx, targety - 4, targetx, targety + 4);

	}

	private static void loadAnimations(String group) {

		// 加载一组动画

		keys.put(group, "");

		DataInputStream in = null;

		try {
			// 读取动画数据
			in = new DataInputStream(GameActivity.assetManager.open("sprite/" + group + ".dt"));

			// ////////////////// 图片 及 切片 数据 ////////////////////

			// 用到的图片数量
			int imgNum = in.read() & 0xff;

			// 创建图片数组
			Bitmap[] imgs = new Bitmap[imgNum];
			// 创建切片数组
			byte[][] slices = new byte[imgNum][];

			// 加载每一张图片 及 其切片数据
			for (int i = 0; i < imgNum; i++) {
				// 图片下标（多余，未使用）
				int k = in.read() & 0xff;
				int index = i;

				// 文件名（包含切片数据和图片的文件.fg)
				String imgName = in.readUTF();

				if (sliceSet.containsKey(imgName)) {
					// 切片数据 和 图片数据 已读取
					slices[index] = (byte[]) sliceSet.get(imgName);
					imgs[index] = (Bitmap) imagesSet.get(imgName);
				} else {
					// 切片数据 和 图片数据 未读取

					// 读取切片和图片数据
					DataInputStream spriteIn = new DataInputStream(GameActivity.assetManager.open("sprite/" + imgName + ".fg"));

					// 切片数量
					short n = spriteIn.readShort();

					// 切片数据 每个切片 6个字节
					// 切片源坐标x,y -> short
					// 切片宽高width,height -> byte
					// slices[index] = new byte[(n << 2) + (n << 1)]; // double
					slices[index] = new byte[n * 8]; // double
					spriteIn.read(slices[index]);

					// 最后读 图片数据
					byte[] imgData = new byte[spriteIn.available()];
					spriteIn.read(imgData);

					// 创建图片
					imgs[index] = BitmapFactory.decodeByteArray(imgData, 0, imgData.length);

					// 储存切片数据
					sliceSet.put(imgName, slices[index]);

					// 储存图片
					if (imgs[index] != null) {
						imagesSet.put(imgName, imgs[index]);
					}

					// 读取完毕
					spriteIn.close();
				}
			}

			// ////////////////// 帧 数据 ////////////////////

			// 帧总数
			int frNum = 0xff & in.readByte();

			// 帧数组
			byte[][] frames = new byte[frNum][];
			// 碰撞区域数组
			byte[][] collision = new byte[frNum][4];
			int[][] whs = new int[frNum][2];
			int[] delays = new int[frNum];

			// 读取每一帧的数据
			for (int i = 0; i < frNum; i++) {
				// 帧下标
				int index = 0xff & in.readByte();
				whs[index][0] = in.readInt(); // 帧宽
				whs[index][1] = in.readInt(); // 帧高
				delays[index] = in.readInt(); // 帧延时毫秒数
				// 碰撞数据
				collision[index][0] = in.readByte();
				collision[index][1] = in.readByte();
				collision[index][2] = in.readByte();
				collision[index][3] = in.readByte();

				// 切片数量
				imgNum = 0xff & in.readByte();
				frames[index] = new byte[imgNum * 7];
				// 每一个切片的数据
				for (int n = 0, m = 0; n < imgNum; n++) {
					frames[index][m++] = in.readByte(); // 图片下标
					frames[index][m++] = in.readByte(); // 切片下标
					frames[index][m++] = in.readByte(); // 目标x 双字节
					frames[index][m++] = in.readByte();
					frames[index][m++] = in.readByte(); // 目标y 双字节
					frames[index][m++] = in.readByte();
					frames[index][m++] = in.readByte(); // 翻转
				}
			}

			// ////////////////// 动画 数据 ////////////////////

			// 动画总数
			int aniCount = in.readByte();

			// 每一个动画的数据
			for (int i = 0; i < aniCount; i++) {
				// 动画ID
				String aniid = in.readUTF();
				int width = in.readInt();
				int height = in.readInt();
				// 动画帧数
				byte frameNum = in.readByte();
				// 每帧延时
				// int delay = in.readByte() & 0xff;
				Animation ani = new Animation(frameNum);
				for (int j = 0; j < frameNum; j++) {
					// 帧下标
					int index = 0xff & in.readByte();

					// 帧数据 和 碰撞数据
					ani.frames[j] = frames[index];
					ani.collision[j] = collision[index];
					ani.whs[j] = whs[index];
					ani.delays[j] = delays[index];
				}
				// ani.delay = delay;
				ani.width = width;
				ani.height = height;

				ani.slicesImage = imgs;
				ani.slices = slices;

				// 储存 动画
				animations.put(group + "_" + aniid, ani);
			}
		} catch (Exception e) {
			e.printStackTrace();

		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {
				}
				in = null;
			}
			System.gc();
		}
	}

	public void resetFrame() {
		frameIndex = 0;
		timer = 0;
		end = false;
	}

	public void setFrameIndex(byte index) {
		frameIndex = index;
	}

	public byte[] getCollision() {
		return collision[frameIndex];
	}

	public int[] getCollided(int dx, int dy, int width, int height) {

		// System.out.println(dx + "," + dy);

		// if (timer > 0) {
		// return null;
		// }

		int x1 = collision[frameIndex][0];
		int x2 = collision[frameIndex][1];

		if (x1 == x2) {
			return null;
		}

		int y1 = collision[frameIndex][2];
		int y2 = collision[frameIndex][3];

		int dx2 = dx + width, dy2 = dy + height;

		if (dx > x2 || dx2 < x1 || dy > y2 || dy2 < y1) {
			return null;
		}

		int[] cols = { x1 < dx ? dx : x1, x2 < dx2 ? x2 : dx2, y1 < dy ? dy : y1, y2 < dy2 ? y2 : dy2 };

		return cols;
	}

	public void setPosition(int x, int y) {
		anix = x;
		aniy = y;
	}

	public void setX(int x) {
		anix = x;
	}

	public void setY(int y) {
		aniy = y;
	}

	public int getX() {
		return anix;
	}

	public int getY() {
		return aniy;
	}

	public static Bitmap getImage(String imgName) {
		return (Bitmap) imagesSet.get(imgName);
	}

	public void replaceImage(Bitmap img, Bitmap newimg) {
		for (int i = 0; i < slicesImage.length; i++) {
			if (slicesImage[i] == img) {
				slicesImage[i] = newimg;
			}
		}
	}

	public boolean isVisible() {
		return visible;
	}

	public void setVisible(boolean visible) {
		this.visible = visible;
	}

	public int getFrameWidth() {
		return frameWidth;
	}

	public int getFrameHeight() {
		return frameHeight;
	}

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

}
